<template>
  <div class="tinymce-box">
    <Editor :id="tinyEditorName" v-model="content" :init="init" tag-name="div" />
    <ImageUpload v-if="showAxupImgs" v-model:showDialog="showAxupImgs" :limit="20" @getImage="insertImage" :isToJpg="true"></ImageUpload>
    <!-- word导入 -->
    <el-dialog v-model="showImportWord" width="680px" title="Word导入" :close-on-click-modal="false" :close-on-press-escape="false">
      <div v-loading="importLoading" element-loading-text="内容导入中...">
        <el-upload
          class="options-item import-word"
          :show-file-list="false"
          :http-request="handleUpload"
          :on-exceed="handleExceed"
          v-model:file-list="wordUploadList"
          :limit="1"
          accept=".docx,.doc"
          drag
        >
          <el-icon class="el-icon--upload"><Plus /></el-icon>
          <div class="el-upload__text">
            将word文件拖动到此处上传 或者
            <em>点击这里</em>
            ,doc格式兼容性较差,推荐使用docx格式
          </div>
          <template #tip>
            <div class="el-upload__tip" style="margin-top: 10px; font-size: 14px">导入后编辑器将插入word中的内容，不支持的样式或图片可能无法展示，请确认后发布</div>
          </template>
        </el-upload>
      </div>
    </el-dialog>
  </div>
  <p class="word-count">正文字数：{{ wordCount }}</p>
</template>

<script>
//引入tinymce编辑器
import Editor from '@tinymce/tinymce-vue'
//引入node_modules里的tinymce相关文件文件
import tinymce from 'tinymce/tinymce' //tinymce默认hidden，不引入则不显示编辑器
import 'tinymce/themes/silver' //编辑器主题，不引入则报错
import 'tinymce/icons/default' //引入编辑器图标icon，不引入则不显示对应图标
import 'tinymce/plugins/advlist' //高级列表
import 'tinymce/plugins/anchor' //锚点
import 'tinymce/plugins/autolink' //自动链接
import 'tinymce/plugins/autoresize' //编辑器高度自适应,注：plugins里引入此插件时，Init里设置的height将失效
import 'tinymce/plugins/autosave' //自动存稿
import 'tinymce/plugins/charmap' //特殊字符
import 'tinymce/plugins/code' //编辑源码
import 'tinymce/plugins/codesample' //代码示例
import 'tinymce/plugins/directionality' //文字方向
import 'tinymce/plugins/emoticons' //表情
import 'tinymce/plugins/fullpage' //文档属性
import 'tinymce/plugins/fullscreen' //全屏
import 'tinymce/plugins/help' //帮助
import 'tinymce/plugins/hr' //水平分割线
import 'tinymce/plugins/image' //插入编辑图片
import 'tinymce/plugins/importcss' //引入css
import 'tinymce/plugins/insertdatetime' //插入日期时间
import 'tinymce/plugins/link' //超链接
import 'tinymce/plugins/lists' //列表插件
import 'tinymce/plugins/media' //插入编辑媒体
import 'tinymce/plugins/nonbreaking' //插入不间断空格
import 'tinymce/plugins/pagebreak' //插入分页符
import 'tinymce/plugins/paste' //粘贴插件
import 'tinymce/plugins/preview' //预览
import 'tinymce/plugins/print' //打印
import 'tinymce/plugins/quickbars' //快速工具栏
import 'tinymce/plugins/save' //保存
import 'tinymce/plugins/searchreplace' //查找替换
import 'tinymce/plugins/tabfocus' //切入切出，按tab键切出编辑器，切入页面其他输入框中
import 'tinymce/plugins/table' //表格
import 'tinymce/plugins/template' //内容模板
import 'tinymce/plugins/textcolor' //文字颜色
import 'tinymce/plugins/textpattern' //快速排版
import 'tinymce/plugins/toc' //目录生成器
import 'tinymce/plugins/visualblocks' //显示元素范围
import 'tinymce/plugins/visualchars' //显示不可见字符
import 'tinymce/plugins/wordcount' //字数统计
import '@npkg/tinymce-plugins/indent2em'
import '/public/tinymce/plugins/layout' //一键布局
import axios from 'axios'

import { watch, ref, computed } from 'vue'
import { importWord } from '@/api/content'
import { convertHexToBase64, parseRtf } from '@/utils/tools'
import { useStore } from 'vuex'
import { ElMessage, ElMessageBox } from 'element-plus'

export default {
  name: 'TEditor',
  components: {
    Editor
  },
  props: {
    editorName: {
      type: String,
      default: 'myEdit'
    },
    title: {
      type: String,
      default: ''
    },
    modelValue: {
      type: String,
      default: ''
    },
    minHeight: {
      type: Number,
      default: 400
    }
  },
  emits: { 'update:modelValue': null },
  setup(props, context) {
    const store = useStore()
    const actionUrl = ref(store.state.content.uploadUrl)
    store.dispatch('content/updateUploadUrl', 1).then(res => {
      actionUrl.value = res.uploadUrl
    })
    // word导入功能
    const wordUploadList = ref([])
    const showImportWord = ref(false)
    const importLoading = ref(false)
    // 粘贴插件预检
    const clip_port = ref(store.state.content.clip_port)
    const handleUpload = async option => {
      let formData = new FormData()
      importLoading.value = true
      formData.append('file', option.file)
      const checkPromise = (url, formData) => {
        return new Promise((resolve, reject) => {
          axios
            .post(url, formData, {
              headers: {
                'Content-Type': 'multipart/form-data'
              }
            })
            .then(res => {
              if (res.data.code) resolve(res)
              else reject()
            })
            .catch(() => {
              reject()
            })
        })
      }
      const precheckUploadPort = () => {
        const importUrl = 'http://127.0.0.1:' + clip_port.value + '/upload'
        return checkPromise(importUrl, formData)
          .then(res => {
            const tempDiv = document.createElement('div')
            tempDiv.id = 'temp-content-div'
            tempDiv.style.display = 'none'
            document.body.appendChild(tempDiv)
            const did = document.getElementById('temp-content-div')
            did.innerHTML = res.data.data
            let styleTag = document.querySelector('#temp-content-div>style')
            let content = document.querySelector('#temp-content-div>div')
            content.style = ''
            // 获取 style 标签中的样式规则
            let styleSheet = styleTag.sheet
            let cssRules = styleSheet.cssRules || styleSheet.rules
            console.log(styleTag, cssRules)
            // 遍历每个样式规则
            Array.from(cssRules).forEach(cssRule => {
              let selectorText = cssRule.selectorText
              let style = cssRule.style
              // 获取匹配样式规则的元素
              let elements = content.querySelectorAll(selectorText)

              // 将样式应用到元素的行内样式
              Array.from(elements).forEach(element => {
                // 应用样式规则到元素的行内样式
                for (let i = 0; i < style.length; i++) {
                  const propName = style[i]
                  const propValue = style.getPropertyValue(propName)
                  if (!element.style[propName]) element.style[propName] = propValue
                }
              })
            })
            // 移除 style 标签
            styleTag.parentNode.removeChild(styleTag)
            tinymce.activeEditor.insertContent(did.innerHTML)
            tinymce.activeEditor.uploadImages()
            did.remove()
            importLoading.value = false
            showImportWord.value = false
          })
          .catch(() => {
            if (clip_port.value < 10089) {
              clip_port.value++
              precheckUploadPort()
            } else {
              importLoading.value = false
              showImportWord.value = false
              const foxClipLink = document.createElement('a')
              foxClipLink.href = 'foxc://'
              foxClipLink.target = '_self'
              foxClipLink.click()
              // 判断用户界面失焦
              let userOnblur = false
              window.onblur = function () {
                userOnblur = true
              }
              setTimeout(() => {
                if (!userOnblur) {
                  ElMessageBox.confirm('<div><p style="font-size: 16px;margin-bottom: 15px;">下载粘贴导入工具，下载文件包内含安装步骤</p></div>', '下载确认', {
                    distinguishCancelAndClose: true,
                    dangerouslyUseHTMLString: true,
                    confirmButtonText: '确认下载',
                    cancelButtonText: '暂不安装（使用原始导入）'
                  })
                    .then(() => {
                      window.open('//api.my120.org/锐狐粘贴插件v2.1.zip')
                    })
                    .catch(async action => {
                      if (action === 'cancel') {
                        let res = await importWord({ file: option.file })
                        showImportWord.value = false
                        tinymce.activeEditor.insertContent(res.data.content)
                      }
                    })
                }
              }, 300)
            }
          })
      }
      precheckUploadPort()
    }
    const handleExceed = () => {
      ElMessage({
        showClose: true,
        message: '只允许上传一个word文件',
        type: 'warning'
      })
    }
    const init = {
      language_url: process.env.BASE_URL + 'tinymce/langs/zh_CN.js', //引入语言包文件
      language: 'zh_CN', //语言类型
      toolbar_sticky: true,
      skin_url: process.env.BASE_URL + 'tinymce/skins/ui/oxide', //皮肤：浅色
      inline: true,
      // custom_ui_selector: "body",
      plugins:
        'autoresize print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount textpattern autosave indent2em layout', //插件配置
      toolbar: [
        'code undo redo | formatselect fontselect fontsizeselect | lineheight bullist numlist | forecolor backcolor bold italic underline strikethrough removeformat layout | alignleft aligncenter alignright alignjustify outdent indent indent2em | axupimgs importWord'
      ], //工具栏配置，设为false则隐藏
      // menubar: 'edit insert view format tools table', //菜单栏配置，设为false则隐藏，不配置则默认显示全部菜单，也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单”
      menubar: false,
      menu: {
        edit: { title: '编辑', items: 'undo redo | cut copy paste pastetext | selectall' },
        insert: { title: '插入', items: 'link image  |  hr' },
        view: { title: '查看', items: 'visualaid' }
      },
      min_height: props.minHeight,
      extended_valid_elements:
        'svg[*],defs[*],pattern[*],desc[*],metadata[*],g[*],mask[*],path[*],line[*],marker[*],rect[*],circle[*],ellipse[*],polygon[*],polyline[*],linearGradient[*],radialGradient[*],stop[*],image[*],view[*],text[*],textPath[*],title[*],tspan[*],glyph[*],symbol[*],switch[*],use[*]',
      fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px', //字体大小
      font_formats:
        '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;', //字体样式
      lineheight_formats: '0.5 0.8 1 1.2 1.5 1.75 2 2.5 3 4 5', //行高配置，也可配置成"12px 14px 16px 20px"这种形式

      placeholder: '在这里输入文字',
      branding: false, //tiny技术支持信息是否显示
      resize: false, //编辑器宽高是否可变，false-否,true-高可变，'both'-宽高均可，注意引号
      statusbar: true,
      elementpath: true, //元素路径是否显示
      fixed_toolbar_container: '#tinymce-box .article-toolbar',
      content_css: process.env.BASE_URL + 'tinymce/tinycontent.css',
      content_style: 'img {max-width:100%;}', //直接自定义可编辑区域的css样式
      paste_data_images: true, //图片是否可粘贴
      convert_urls: false,
      layout_options: {
        style: {
          'text-indent': '2em',
          'line-height': '2',
          'font-family': '微软雅黑',
          'font-size': '16px'
        },
        filterTags: ['table>*', 'tbody>*'],
        clearStyle: ['position', 'top', 'bottom'],
        tagsStyle: {
          'p,p>span,span': {
            'text-indent': '2em',
            'line-height': '2',
            'font-family': '微软雅黑',
            'font-size': '16px'
          },
          font: { 'font-family': '微软雅黑' },
          img: { display: 'block', 'margin-left': 'auto', 'margin-right': 'auto', width: '800px', height: 'auto' }
        }
      },
      // eslint-disable-next-line no-unused-vars
      images_upload_handler: (blobInfo, success, failure, progress) => {
        // 方法1：用base64的图片形式上传图片
        // const img = 'data:image/jpeg;base64,' + blobInfo.base64()
        // success(img)

        // 方法2：上传oos
        const xhr = new XMLHttpRequest()
        xhr.withCredentials = false
        xhr.open('POST', actionUrl.value)
        xhr.onload = function () {
          if (xhr.status === 403) {
            if (process.env.NODE_ENV === 'development') console.log('HTTP Error: ' + xhr.status)
            return
          }
          if (xhr.status < 200 || xhr.status >= 300) {
            if (process.env.NODE_ENV === 'development') console.log('HTTP Error: ' + xhr.status)
            return
          }
          //注意json
          const json = JSON.parse(xhr.responseText)
          if (json.url && json.url != '') {
            success(json.url)
          }
        }

        xhr.onerror = function () {
          failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status)
        }

        let formData = new FormData()
        formData.append('name', blobInfo.filename())
        formData.append('file', blobInfo.blob(), blobInfo.filename())
        xhr.send(formData)
      },
      init_instance_callback: function (editor) {
        editor.focus()
        let tinyCnt = tinymce.activeEditor
        wordCount.value = tinyCnt.plugins.wordcount.getCount()
        editor.on('paste', evt => {
          const clipboardData = evt.clipboardData
          const isImage = clipboardData.types.length && clipboardData.types.join('').includes('Files')
          if (!isImage) {
            //------------------------粘贴word核心代码开始-------------------------
            const hexStrings = parseRtf(evt.clipboardData.getData('text/rtf'))
            // 获取base64图片数据
            const base64Images = hexStrings.map(hexObj => {
              return convertHexToBase64(hexObj.hex)
            })
            setTimeout(() => {
              const wordImages = tinymce.activeEditor.dom.doc.querySelectorAll('img[src*="file://"]')
              wordImages.forEach((item, index) => {
                item.src = `data:${hexStrings[index].type};base64,${base64Images[index]}`
                tinymce.activeEditor.uploadImages()
              })
            }, 200)
            //------------------------粘贴word核心代码结束-------------------------
            return
          }
          const file = clipboardData.files[0]
          if (
            !file ||
            !file.name ||
            !(
              file.name.toLowerCase().indexOf('.png') !== -1 ||
              file.name.toLowerCase().indexOf('.gif') !== -1 ||
              file.name.toLowerCase().indexOf('.jpeg') !== -1 ||
              file.name.toLowerCase().indexOf('.jpg') !== -1
            )
          ) {
            this.$message.error('您粘贴的不是图片请检查！')
            return
          }
          if (file.size / 1024 / 1024 > 5) {
            this.$message.error('上传失败，图片大小请控制在 5M 以内')
            return
          }
        })
      },
      setup: editor => {
        editor.ui.registry.addButton('axupimgs', {
          text: `<i class="iconfont">&#xe609;</i>`,
          tooltip: '批量上传',
          onAction: () => {
            showAxupImgs.value = true
          }
        })
        editor.ui.registry.addButton('importWord', {
          text: `<i class="iconfont">&#xe6f5;</i>`,
          tooltip: 'word导入',
          onAction: () => {
            showImportWord.value = true
            wordUploadList.value = []
          }
        })
        // editor.on('change setContent',function() {
        //   if(tinymce.activeEditor) {
        //     let tinyCnt = tinymce.activeEditor
        //     wordCount.value = tinyCnt.plugins.wordcount.getCount();
        //     console.log(333,tinyCnt)
        //   }
        // })
      }
    }
    tinymce.init
    const wordCount = ref(0)
    const tinyEditorName = computed(() => {
      return props.editorName
    })
    const revert_data = content => {
      context.emit('update:modelValue', content.value)
    }
    const content = ref(props.modelValue)
    const modelTitle = ref(props.title)

    watch(
      () => props.modelValue,
      () => {
        content.value = props.modelValue
      }
    )
    watch(
      () => content.value,
      () => {
        let tinyCnt = tinymce.activeEditor
        wordCount.value = tinyCnt.plugins.wordcount.getCount()
        revert_data(content)
      }
    )

    const showAxupImgs = ref(false)
    const insertImage = imgList => {
      let imgContent = ``
      imgList.forEach(item => {
        let _img = `<p><img src="${item.url}" /></p>`
        imgContent = imgContent + _img
      })
      tinymce.editors[tinyEditorName.value].insertContent(imgContent + '<p></p>')
    }

    return {
      content,
      modelTitle,
      init,
      tinyEditorName,
      showAxupImgs,
      insertImage,
      wordCount,
      handleUpload,
      handleExceed,
      importLoading,
      showImportWord,
      wordUploadList
    }
  }
}
</script>

<style lang="scss" scoped>
.tinymce-box {
  width: 100%;
  padding-bottom: 20px;
  border-bottom: 1px solid #ccc;
  .editor-header {
    height: 60px;
    line-height: 60px;
    h2 {
      font-size: 18px;
      color: #333333;
    }
  }
}
.word-count {
  font-size: 14px;
  color: #a2a1a1;
  line-height: 1;
  margin-top: 10px;
}
::v-deep(.axupimg-box .el-dialog__body) {
  padding: 0;
}
.axupimg-box {
  .inner-axupimg-box {
    display: flex;
    height: 450px;
    .axupimg-upload-box {
      width: 520px;
      height: 100%;
      .local-upload {
        padding: 10px;
        width: 500px;
        .local-upload-list {
          height: 330px;
          display: flex;
          flex-wrap: wrap;
          position: relative;
          align-content: flex-start;
          & > div {
            height: 110px;
            width: 110px;
            margin-right: 10px;
            margin-bottom: 10px;
            background-color: #f0f2f7;
          }
          li {
            height: 110px;
            width: 110px;
            box-sizing: border-box;
            list-style: none;
            border: 1px solid #dcdfe6;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
            border-radius: 5px;
            cursor: pointer;
            .img-box {
              height: 108px;
              width: 108px;
              display: flex;
              justify-content: center;
              align-items: center;
              img {
                max-width: 108px;
                max-height: 108px;
              }
            }
            .progress-box {
              width: 100px;
              position: absolute;
              bottom: 5px;
            }
            .remove-upload {
              color: $placeholderColor;
              position: absolute;
              right: 5px;
              top: 8px;
              cursor: pointer;
              display: none;
            }
            .choose-check {
              position: absolute;
              left: 5px;
              top: 0;
            }
            &:hover {
              .remove-upload {
                display: block;
              }
            }
            &.is-choose {
              border-color: $mainColor;
            }
          }
        }
      }
      .online-upload {
        padding: 10px;
        width: 500px;
        .online-upload-list {
          display: flex;
          flex-wrap: wrap;
          height: 375px;
          .online-upload-item {
            height: 115px;
            width: 115px;
            border-radius: 6px;
            background-color: $borderColor;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 10px;
            margin-bottom: 10px;
            cursor: pointer;
            box-sizing: border-box;
            img {
              max-width: 100%;
              max-height: 100%;
            }
            &:nth-child(4n) {
              margin-right: 0;
            }
            &.is-choose {
              border: 1px solid $mainColor;
            }
          }
        }
      }
    }
    .axupimg-list-box {
      width: 180px;
      height: 100%;
      border-left: 1px solid #dcdfe6;
      box-sizing: border-box;
      padding: 10px;
      p {
        margin-bottom: 10px;
      }
      .chose-img-list {
        height: 400px;
        justify-content: center;
        flex-direction: column;
        .chose-img-box {
          height: 130px;
          width: 145px;
          display: flex;
          align-items: center;
          justify-content: center;
          background-color: #f0f2f7;
          margin-bottom: 10px;
          border-radius: 6px;
          img {
            max-height: 100%;
            max-width: 100%;
          }
        }
      }
    }
  }
}
</style>
